package com.liangtian.mdm.dm.jsp.service.imp;

import com.liangtian.mdm.dm.attribute.dao.AttributeDao;
import com.liangtian.mdm.dm.attribute.model.Attribute;
import com.liangtian.mdm.dm.config.model.AttrConfig;
import com.liangtian.mdm.dm.jsp.dao.JspActionDao;
import com.liangtian.mdm.dm.jsp.service.AutoCodeService;
import com.liangtian.mdm.dm.listener.CacheUtil;
import com.liangtian.mdm.dm.model.dao.ModelDao;
import com.liangtian.mdm.dm.model.model.Model;
import com.liangtian.mdm.system.code.dao.CodeDao;
import com.liangtian.mdm.system.code.model.Code;
import com.liangtian.mdm.util.StringUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * function:自动附码
 * date:0329
 *
 * @author wy
 */

@Service
public class AutoCodeServiceImpl implements AutoCodeService {

    @Autowired
    private CacheUtil cacheUtil;
    @Autowired
    private CodeDao codeDao;
    @Autowired
    AttributeDao attributeDao;
    @Autowired
    private JspActionDao jspActionDao;
    @Autowired
    private ModelDao modelDao;

    private String codeFormatPre; //= "";//流水码位置之前的编码
    private String codeFormatBeh; //= "";//流水码位置之后的编码
    private boolean isPreOrBehLsm; //= false;//用来标识流水码
    private int[] lsm;//流水码位数,流水码起始值,流水码增量
    private boolean hasLSM;//用来标识是否有流水码,true:有流水码

    /**
     * 调用此方法自动附码
     * 参数:modelId:模型ID  dataId:数据ID
     */
    @Override
    public String[] autoCode(String modelId, String dataid) {//String modelId,String attrId,String dataId
        String[] finArr = new String[2];//[0]:用来标识生成的最终编码  , [1]:用来标识生成编码的过程中报错信息,如果[1]不为空,则认为附码失败
        List<Attribute> attrList = attributeDao.queryAttributeFromConfig(modelId);//查询需要自动附码的属性集
        if (CollectionUtils.isEmpty(attrList)) {
            //finArr[1] = "当前主数据没有需要自动附码的属性,";
//		}else if(attrList.size()>1){
//			finArr[1] = "需要自动附码的属性多余1个,";
        } else {
            Model model = modelDao.selectByPk(modelId);

            Map<String, Object> map = new HashMap<>();
            map.put("tableName", model.getTableName());
            map.put("id", dataid);
            Map<String, Object> currentData = jspActionDao.queryOne(map);//根据表名和数据ID获取当前数据的信息

            if (MapUtils.isEmpty(currentData)) {
                finArr[1] = "没有查询出来符合条件的数据,";
            } else {
                Map<String, String> codeMap = new HashMap<String, String>();//当前主数据一共配置了N个属性需要自动生成编码,这个map用来临时存放这N个属性以及生成的对应编码,以键值对的形式
                for (Attribute attr : attrList) {//循环需要附码的属性,获取到每个属性生成的编码
                    //合同编码会根据大类小类重新赋码
                    if (ObjectUtils.isEmpty(currentData.get(attr.getAttrColumn().toLowerCase())) || "1925".equals(modelId)) {
                        finArr = autoMain(model, attr, currentData);//根据具体的某一个属性设置的编码规则  生成编码
                        System.out.println(finArr[0] + finArr[1]);
                        if (org.apache.commons.lang3.StringUtils.isEmpty(finArr[1])) {
                            codeMap.put(attr.getAttrColumn(), finArr[0]);
                        } else {//如果有多个属性需要自动附码,其中有任何一个属性生成编码失败,则不再继续给别的属性生成编码,直接返回当前属性生成编码失败的原因,跳出循环
                            break;
                        }
                    }
                }
                if (StringUtils.isEmpty(finArr[1]) && !StringUtils.isEmpty(finArr[0])) {//所有需要自动附码的属性都已经正常生成编码之后才会执行这个方法
                    finArr = updateDBCode(codeMap, model.getTableName(), dataid);
                }
            }
        }
        return finArr;
    }

    /**
     * 所有需要自动附码的属性都已经根据编码规则正常生成编码,则调用此方法,update数据库更新当前数据
     *
     * @param maps       : 需要自动附码的所有属性名和对应的编码    maps<attr:codeVal>
     * @param tableName: 当前模型的表名
     * @param dataid     : 当前需要更新的数据ID
     * @return
     */
    private String[] updateDBCode(Map<String, String> maps, String tableName, String dataid) {
        Map<String, String> map = new HashMap<String, String>();
        String[] fArr = new String[2];
        try {
            String codeCol = "";
            for (Map.Entry<String, String> entry : maps.entrySet()) {
                codeCol += entry.getKey() + " = '" + entry.getValue() + "',";
                fArr[0] = entry.getValue();
            }
            map.put("column", codeCol.substring(0, codeCol.length() - 1));
            map.put("tableName", tableName);
            map.put("dataid", dataid);
            jspActionDao.updateCodeFromAuto(map);
        } catch (Exception e) {
            fArr[1] = "更新数据库失败,";
        }
        return fArr;
    }

    /*
     * 获取编码
     * 返回结果:string[]
     * 参数:string[0]:编码
     *     String[1]:错误信息
     */
    private String[] autoMain(Model model, Attribute attr, Map<String, Object> currentData) {
        codeFormatPre = "";
        codeFormatBeh = "";
        isPreOrBehLsm = false;
        hasLSM = false;
        lsm = new int[3];
        String[] retStr = new String[2];

        Map<String, Object> param = new HashMap<>();
        param.put("modelId", model.getId());
        param.put("attrId", attr.getId());
        List<Code> codeList = codeDao.selectCodeConfigs(param);//查询am_code_config表得到当前主数据的编码规则,接下来解析编码规则获取编码

        for (Code code : codeList) {
            try {
                String splitVal = splitCodeRule(code, model, currentData);
                //晋煤合同日期流水码每年重新开始
                if ("dateCode".equals(code.getCodeType())) {
                    codeFormatPre += StringUtil.dateToString("yyyy", new Date());
                } else {
                    fillPreOrBehVal(splitVal);
                }
            } catch (Exception e) {
                retStr[1] = e.getMessage();
                e.printStackTrace();
            }
        }


        if (StringUtils.isEmpty(retStr[1])) {
            try {
                retStr[0] = getFinalCode(attr, model);
            } catch (Exception e) {
                retStr[1] = e.getMessage();
            }
        }

        return retStr;
    }

    /*
     * 根据生成的不带流水码的编码查询数据库     获取最终编码
     * 参数:attr:属性
     *     model:当前模型
     * 返回:最终的编码
     */
    private synchronized String getFinalCode(Attribute attr, Model model) throws Exception {
        String val = codeFormatPre;//如果当前需要附码的属性没有流水码,则生成的最后编码就是codeFormatPre,     即codeFormatBeh=null,
        String codeFormatCenter = "";

        if (hasLSM) {
            val = codeFormatPre + "%" + codeFormatBeh;//根据编码规则(除去流水码)拼接的编码
            Map<String, String> map = new HashMap<String, String>();
            map.put("column", attr.getAttrColumn());
            map.put("tableName", model.getTableName());
            map.put("columnVal", val);
            map.put("codeLength", (val.length() - 1 + lsm[0]) + "");
            String existcode = jspActionDao.queryMaxLsm(map);//根据拼接的编码查看当前数据库中匹配此编码的最大的流水码

            if (StringUtils.isEmpty(existcode)) {//如果当前不存在除了流水码之外的编码(like)
                codeFormatCenter = String.format("%0" + lsm[0] + "d", lsm[1]);
            } else {
                String runnPre = existcode.substring(codeFormatPre.length(), existcode.length());
                String runn = runnPre.substring(0, runnPre.length() - codeFormatBeh.length());
                codeFormatCenter = String.format("%0" + lsm[0] + "d", Integer.parseInt(runn) + lsm[2]);//lsm
            }
            if (codeFormatCenter.length() > lsm[0]) {
                throw new Exception("流水码已满,");
            } else {
                val = codeFormatPre + codeFormatCenter + codeFormatBeh;
            }
        }


        return val;
    }

    /*
     * 解析编码规则
     */
    private String splitCodeRule(Code code, Model model, Map<String, Object> currentData) throws Exception {
        String splitCode = "";
        if ("timeCode".equals(code.getCodeType())) {//时间码
            splitCode = timeCodeRule(code);
        } else if ("dateCode".equals(code.getCodeType())) {//日期码
            splitCode = dateCodeRule(code);
        } else if ("sjCode".equals(code.getCodeType())) {//随机码
            splitCode = String.valueOf(sjCodeRule(code));
        } else if ("hyCode".equals(code.getCodeType())) {//含义码
            splitCode = hyCodeRule(code, model, currentData);
        } else if ("fixCode".equals(code.getCodeType())) {//固定值码
            splitCode = fixCodeRule(code);
        } else if ("lsCode".equals(code.getCodeType())) {//流水码
            lsCodeRule(code);
        }
        return splitCode;
    }

    //时间码
    private String timeCodeRule(Code code) throws Exception {
        String str = "";
        if ("systime".equals(code.getValueType())) {
            str = StringUtil.dateToString("HHmm", new Date());
        } else {
            str = code.getCodeValue();
            if (null == str || "".equals(str)) {
                throw new Exception("时间码为空");
            }
        }
        return str;
    }

    //日期码
    private String dateCodeRule(Code code) throws Exception {
        String str = "";
        if ("sysdate".equals(code.getValueType())) {
            //str = StringUtils.dateToString("yyyyMMdd",new Date());
            //晋煤合同类日期要求只要年份
            str = StringUtil.dateToString("yyyy", new Date());
        } else {
            str = code.getCodeValue();
            if (null == str || "".equals(str)) {
                throw new Exception("日期码为空");
            }
        }
        return str;
    }

    //流水码
    private void lsCodeRule(Code code) throws Exception {
        int codeValLen = Integer.parseInt(code.getValueLength());
        if (codeValLen < 1) {
            throw new Exception("流水码位数最少为1");
        } else {
            lsm[0] = codeValLen;
            lsm[1] = Integer.parseInt(code.getStartValue());
            lsm[2] = Integer.parseInt(code.getIncrementValue());
            isPreOrBehLsm = true;
            hasLSM = true;
        }
    }

    //随机码
    private long sjCodeRule(Code code) throws Exception {
        int len = Integer.parseInt(code.getValueLength());
        if (len < 1) {
            throw new IllegalArgumentException("随机数位数必须大于0");
        }
        return (long) (Math.random()*9*Math.pow(10, len - 1)) + (long) Math.pow(10, len - 1);
    }

    //固定值码
    private String fixCodeRule(Code code) throws Exception {
        String codeValue = code.getCodeValue();
        if (null == codeValue || "".equals(codeValue)) {
            throw new Exception("固定值码为空");
        }
        return codeValue;
    }

    /*
     * ---含义码---(表:AM_CODE_CONFIG)
     * 含义码取值逻辑:根据config_code_field字段判断是否需要去关联表里查询,如果此字段为空,则标识不需要去关联表里查询对应的值,直接取对应属性的值即可
     * 如果此字段有值,则标识需要去含义码对应的属性的关联表里取对应的属性(含义码对应的属性:code_value字段,关联的表:code_value属性关联的表(AM_ATTR_CONFIG表中的dataSourceTable字段))
     */
    private String hyCodeRule(Code code, Model model, Map<String, Object> currentData) throws Exception {
        String codeValue = code.getCodeValue();
        String attr = codeValue.split("--")[1];
        String configCodeField = code.getConfigCodeField();//如果有值,则说明是关联属性,需要继续查询关联表才能取到正确的值
        Map<String, AttrConfig> ma = cacheUtil.getAttrMap(model.getId());//AM_ATTR_CONFIG---属性设置详细信息
        AttrConfig ac = ma.get(attr);
        String val = (String) currentData.get(attr.toLowerCase());
        if (StringUtils.isEmpty(val)) {
            throw new Exception("'含义码'关联的值为空,");
        }
        if (null != configCodeField && !"".equals(configCodeField)) {//如果不为空,则需要关联取值---根据id去关联表里取值
            String dateSourceTable = ac.getDataSourceTable();
            Map<String, Object> map = new HashMap<>();
            map.put("tableName", "T_" + dateSourceTable);
            map.put("id", val);
            Map<String, Object> relationData = jspActionDao.queryOne(map);
            val = (String) relationData.get(configCodeField.toLowerCase());
        }
        return val;
    }

    // 流水码在整个编码中的位置,拼接编码
    private void fillPreOrBehVal(String codeVal) {
        if (isPreOrBehLsm) {
            codeFormatBeh += codeVal;
        } else {
            codeFormatPre += codeVal;
        }
    }

}
